package net.cellkyborg.server;
public class stream {

	public stream() {
		buffer = new byte [256];
		writeOffset = 0;
		readOffset = 0;
	}

	public stream(byte abyte0[]) {
		buffer = abyte0;
		writeOffset = 0;
		readOffset = 0;
	}
	
	public void writeSize(int packetSize) {
		buffer[writeOffset++] = (byte)(packetSize >> 8);
		buffer[writeOffset++] = (byte)(packetSize);
	}
	
	public int readSize() {
		return ((readUnsignedByte()<<8) + readUnsignedByte());
	}

	public byte readSignedByteA() {
		return (byte)(buffer[readOffset++] - 128);
	}

	public byte readSignedByteC() {
		return (byte)(-buffer[readOffset++]);
	}

	public byte readSignedByteS() {
		return (byte)(128 - buffer[readOffset++]);
	}

	public int readUnsignedByteA() {
		return buffer[readOffset++] - 128 & 0xff;
	}

	public int readUnsignedByteC() {
		return -buffer[readOffset++] & 0xff;
	}

	public int readUnsignedByteS() {
		return 128 - buffer[readOffset++] & 0xff;
	}

	public void writeByteA(int i) {
		buffer[writeOffset++] = (byte)(i + 128);
	}

	public void writeByteS(int i) {
		buffer[writeOffset++] = (byte)(128 - i);
	}

	public void writeByteC(int i) {
		buffer[writeOffset++] = (byte)(-i);
	}

	public int readSignedWordBigEndian() {
		readOffset += 2;
		int i = ((buffer[readOffset - 1] & 0xff) << 8) + (buffer[readOffset - 2] & 0xff);
		if(i > 32767) {
			i -= 0x10000;
		}
		return i;
	}

	public int readSignedWordA() {
		readOffset += 2;
		int i = ((buffer[readOffset - 2] & 0xff) << 8) + (buffer[readOffset - 1] - 128 & 0xff);
		if(i > 32767) {
			i -= 0x10000;
		}
		return i;
	}

	public int readSignedWordBigEndianA() {
		readOffset += 2;
		int i = ((buffer[readOffset - 1] & 0xff) << 8) + (buffer[readOffset - 2] - 128 & 0xff);
		if(i > 32767)
			i -= 0x10000;
		return i;
	}

	public int readUnsignedWordBigEndian() {
		readOffset += 2;
		return ((buffer[readOffset - 1] & 0xff) << 8) + (buffer[readOffset - 2] & 0xff);
	}

	public int readUnsignedWordA() {
		readOffset += 2;
		return ((buffer[readOffset - 2] & 0xff) << 8) + (buffer[readOffset - 1] - 128 & 0xff);
	}

	public int readUnsignedWordBigEndianA() {
		readOffset += 2;
		return ((buffer[readOffset - 1] & 0xff) << 8) + (buffer[readOffset - 2] - 128 & 0xff);
	}

	public void writeWordBigEndianA(int i) {
		buffer[writeOffset++] = (byte)(i + 128);
		buffer[writeOffset++] = (byte)(i >> 8);
	}

	public void writeWordA(int i) {
		buffer[writeOffset++] = (byte)(i >> 8);
		buffer[writeOffset++] = (byte)(i + 128);
	}

	public void writeWordBigEndian_dup(int i) {
		buffer[writeOffset++] = (byte)i;
		buffer[writeOffset++] = (byte)(i >> 8);
	}

	public int readDWord_v1() {
		readOffset += 4;
		return ((buffer[readOffset - 2] & 0xff) << 24) + ((buffer[readOffset - 1] & 0xff) << 16) + ((buffer[readOffset - 4] & 0xff) << 8) + (buffer[readOffset - 3] & 0xff);
	}

	public int readDWord_v2() {
		readOffset += 4;
		return ((buffer[readOffset - 3] & 0xff) << 24) + ((buffer[readOffset - 4] & 0xff) << 16) + ((buffer[readOffset - 1] & 0xff) << 8) + (buffer[readOffset - 2] & 0xff);
	}

	public void writeDWord_v1(int i) {
		buffer[writeOffset++] = (byte)(i >> 8);
		buffer[writeOffset++] = (byte)i;
		buffer[writeOffset++] = (byte)(i >> 24);
		buffer[writeOffset++] = (byte)(i >> 16);
	}

	public void writeDWord_v2(int i) {
		buffer[writeOffset++] = (byte)(i >> 16);
		buffer[writeOffset++] = (byte)(i >> 24);
		buffer[writeOffset++] = (byte)i;
		buffer[writeOffset++] = (byte)(i >> 8);
	}
	
	public void writeDWord_v3(int i) {
		buffer[writeOffset++] = (byte)(i >> 24);
		buffer[writeOffset++] = (byte)i;
		buffer[writeOffset++] = (byte)(i >> 16);
		buffer[writeOffset++] = (byte)(i >> 8);
	}

	public void readBytes_reverse(byte abyte0[], int i, int j) {
		for(int k = (j + i) - 1; k >= j; k--) {
			abyte0[k] = buffer[readOffset++];

		}
	}
/**
 * 
 * @param abyte0 - an array to be written
 * @param i - length of the data to be written
 * @param j - offset in the abyte0[]
 */
	public void writeBytes_reverse(byte abyte0[], int i, int j) {
		for(int k = (j + i) - 1; k >= j; k--)
			buffer[writeOffset++] = abyte0[k];

	}

	public void readBytes_reverseA(byte abyte0[], int i, int j) {
		for(int k = (j + i) - 1; k >= j; k--)
			abyte0[k] = (byte)(buffer[readOffset++] - 128);

	}

	public void writeBytes_reverseA(byte abyte0[], int i, int j) {
		for(int k = (j + i) - 1; k >= j; k--)
			buffer[writeOffset++] = (byte)(abyte0[k] + 128);

	}

	public void createFrame(int id) {
		buffer[writeOffset++] = (byte)(id + packetEncryption.getNextKey());
	}

	private static final int frameStackSize = 100;
	private int frameStackPtr = -1;
	private int frameStack[] = new int[frameStackSize];
	public void createFrameVarSize(int id){ // creates a variable sized frame
		buffer[writeOffset++] = (byte)(id + packetEncryption.getNextKey());
		buffer[writeOffset++] = 0;		// placeholder for size byte
		if(frameStackPtr >= frameStackSize-1) {
			throw new RuntimeException("Stack overflow");
		}
		else frameStack[++frameStackPtr] = writeOffset;
	}
	public void createFrameVarSizeWord(int id) { // creates a variable sized frame
		buffer[writeOffset++] = (byte)(id + packetEncryption.getNextKey());
		writeWord(0);		// placeholder for size word
		if(frameStackPtr >= frameStackSize-1) {
			throw new RuntimeException("Stack overflow");
		}
		else frameStack[++frameStackPtr] = writeOffset;
	}

	public void endFrameVarSize() {// ends a variable sized frame
		if(frameStackPtr < 0) throw new RuntimeException("Stack empty");
		else writeFrameSize(writeOffset - frameStack[frameStackPtr--]);
	}

	public void endFrameVarSizeWord() { // ends a variable sized frame
		if(frameStackPtr < 0) throw new RuntimeException("Stack empty");
		else writeFrameSizeWord(writeOffset - frameStack[frameStackPtr--]);
	}
	
	public void writeByte(int i) {
		buffer[writeOffset++] = (byte)i;
	}

	public void writeWord(int i) {
		buffer[writeOffset++] = (byte)(i >> 8);
		buffer[writeOffset++] = (byte)i;
	}

	public void writeWordBigEndian(int i) {
		buffer[writeOffset++] = (byte)i;
		buffer[writeOffset++] = (byte)(i >> 8);
	}

	public void write3Byte(int i) {
		buffer[writeOffset++] = (byte)(i >> 16);
		buffer[writeOffset++] = (byte)(i >> 8);
		buffer[writeOffset++] = (byte)i;
	}

	public void writeDWord(int i) {
		buffer[writeOffset++] = (byte)(i >> 24);
		buffer[writeOffset++] = (byte)(i >> 16);
		buffer[writeOffset++] = (byte)(i >> 8);
		buffer[writeOffset++] = (byte)i;
	}

	public void writeDWordBigEndian(int i) {
		buffer[writeOffset++] = (byte)i;
		buffer[writeOffset++] = (byte)(i >> 8);
		buffer[writeOffset++] = (byte)(i >> 16);
		buffer[writeOffset++] = (byte)(i >> 24);
	}

	public void writeQWord(long l) {
		buffer[writeOffset++] = (byte)(int)(l >> 56);
		buffer[writeOffset++] = (byte)(int)(l >> 48);
		buffer[writeOffset++] = (byte)(int)(l >> 40);
		buffer[writeOffset++] = (byte)(int)(l >> 32);
		buffer[writeOffset++] = (byte)(int)(l >> 24);
		buffer[writeOffset++] = (byte)(int)(l >> 16);
		buffer[writeOffset++] = (byte)(int)(l >> 8);
		buffer[writeOffset++] = (byte)(int)l;
	}

	public void writeString(java.lang.String s) {
		s.getBytes(0, s.length(), buffer, writeOffset);
		writeOffset += s.length();
		buffer[writeOffset++] = 0;
	}
/**
 * 
 * @param abyte0 Source byte array
 * @param i Length of data to be written
 * @param j Offset start from
 */
	public void writeBytes(byte abyte0[], int i, int j) {
		for(int k = j; k < j + i; k++)
			buffer[writeOffset++] = abyte0[k];

	}

	public void writeFrameSize(int i) {
		buffer[writeOffset - i - 1] = (byte)i;
	}

	public void writeFrameSizeWord(int i) {
		buffer[writeOffset - i - 2] = (byte)(i >> 8);
		buffer[writeOffset - i - 1] = (byte)i;
	}

	public int readUnsignedByte() {
		return buffer[readOffset++] & 0xff;
	}

	public byte readSignedByte() {
		return buffer[readOffset++];
	}

	public int readUnsignedWord() {
		readOffset += 2;
		return ((buffer[readOffset - 2] & 0xff) << 8) + (buffer[readOffset - 1] & 0xff);
	}

	public int readSignedWord() {
		readOffset += 2;
		int i = ((buffer[readOffset - 2] & 0xff) << 8) + (buffer[readOffset - 1] & 0xff);
		if(i > 32767) {
			i -= 0x10000;
		}
		return i;
	}

	public int readDWord() {
		readOffset += 4;
		return ((buffer[readOffset - 4] & 0xff) << 24) + ((buffer[readOffset - 3] & 0xff) << 16) + ((buffer[readOffset - 2] & 0xff) << 8) + (buffer[readOffset - 1] & 0xff);
	}
	public int readDWordBigEndian() {
		readOffset += 4;
		return ((buffer[readOffset - 1] & 0xff) << 24) + ((buffer[readOffset - 2] & 0xff) << 16) + ((buffer[readOffset - 3] & 0xff) << 8) + (buffer[readOffset - 4] & 0xff);
	}

	public long readQWord() {
		long l = (long)readDWord() & 0xffffffffL;
		long l1 = (long)readDWord() & 0xffffffffL;
		return (l << 32) + l1;
	}
	public long readQWord2() {
		readOffset += 8;
		return (((buffer[readOffset - 8] & 0xff) << 56) + ((buffer[readOffset - 7] & 0xff) << 48) + ((buffer[readOffset - 6] & 0xff) << 40) + ((buffer[readOffset - 5] & 0xff) << 32) + ((buffer[readOffset - 4] & 0xff) << 24) + ((buffer[readOffset - 3] & 0xff) << 16) + ((buffer[readOffset - 2] & 0xff) << 8) + (buffer[readOffset - 1] & 0xff));
	}

	public String readString() {
		int i = readOffset;
		while(buffer[readOffset++] != 0) if (readOffset>=writeOffset) return null;//10
		return new String(buffer, i, readOffset - i - 1);
	}
/**
 * 
 * @param abyte0 Output array
 * @param i Lenght
 * @param j Write offset
 */
	public void readBytes(byte abyte0[], int i, int j) {
		for(int k = j; k < j + i; k++)
			abyte0[k] = buffer[readOffset++];

	}

	public void initBitAccess() {
		//bitPosition = currentOffset * 8;
		bitPosition = writeOffset * 8;
	}

	public void writeBits(int numBits, int value) {
		int bytePos = bitPosition >> 3;
		int bitOffset = 8 - (bitPosition & 7);
		bitPosition += numBits;

		for(; numBits > bitOffset; bitOffset = 8) {
			buffer[bytePos] &= ~ bitMaskOut[bitOffset];		// mask out the desired area
			buffer[bytePos++] |= (value >> (numBits-bitOffset)) & bitMaskOut[bitOffset];

			numBits -= bitOffset;
		}
		if(numBits == bitOffset) {
			buffer[bytePos] &= ~ bitMaskOut[bitOffset];
			buffer[bytePos] |= value & bitMaskOut[bitOffset];
		} else {
			buffer[bytePos] &= ~ (bitMaskOut[numBits]<<(bitOffset - numBits));
			buffer[bytePos] |= (value&bitMaskOut[numBits]) << (bitOffset - numBits);
		}
	}

	public void finishBitAccess() {
		writeOffset = (bitPosition + 7) / 8;
	}	
	
	public int available() {
		return (writeOffset-readOffset);
	}
	
	public byte buffer[] = null;
	public int writeOffset = 0;
	public int readOffset = 0;
	public int bitPosition = 0;

	public static int bitMaskOut[] = new int[32];
	
	static {
		for(int i = 0; i < 32; i++)
			bitMaskOut[i] = (1 << i) - 1;
	}

	public Cryption packetEncryption = null;

}
